#pragma once
#include "onlineUsers.hpp"
#include "util.hpp"
#include "dbexec.hpp"

#include <iostream>
#include <vector>
#include <memory>

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

// 房间类的设计（管理的数据 & 处理的动作）

typedef enum
{
    GAME_START,
    GAME_OVER,
} room_state;

class RoomOnlineManger
{
private:
    uint64_t _room_id;
    room_state _room_state;                 // 房间的状态
    int _room_user_nums;                    // 玩家数量
    uint64_t _white_user_id;                // 白棋玩家id
    uint64_t _black_user_id;                // 黑棋玩家id
    user_exec *_user_exec;                  // 用户信息表管理的句柄  通过指针管理
    OlineUser_Manager *_onlineUser_manager; // 在线用户管理的句柄
    std::vector<std::vector<int>> _board;   // 棋盘

private:
    bool is_five(int row, int col, int row_offset, int col_offset, int color)
    {
        int count = 1; // 记录同色棋子个数
        int search_row = row + row_offset;
        int search_col = col + col_offset;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            count++;
            search_row += row_offset;
            search_col += col_offset;
        }

        // 反向
        search_row = row - row_offset;
        search_col = col - col_offset;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            count++;
            search_row -= row_offset;
            search_col -= col_offset;
        }

        return count >= 5;
    }
    // 检查这一步棋是否有玩家获胜,有返回玩家id,无返回0
    uint64_t check_win(int row, int col, int color)
    {
        // 分别4个方向判断是否五星连珠
        if (is_five(row, col, 0, 1, color) ||
            is_five(row, col, 1, 0, color) ||
            is_five(row, col, -1, 1, color) ||
            is_five(row, col, -1, -1, color))
        {
            return color == CHESS_WHITE ? _white_user_id : _black_user_id;
        }

        return 0;
    }

public: // 房间动作管理
    RoomOnlineManger(uint64_t room_id, user_exec *user_exec, OlineUser_Manager *online_user)
        : _room_id(room_id), _room_state(GAME_START), _room_user_nums(0),
          _user_exec(user_exec), _onlineUser_manager(online_user),
          _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0))
    {
        DLOG("%lu 房间创建成功！！！", _room_id);
    }

    ~RoomOnlineManger()
    {
        DLOG("%lu 房间销毁成功！！！", _room_id);
    }

    RoomOnlineManger()
    {
    }

    // 一批小接口
    uint64_t get_room_id()
    {
        return _room_id;
    }

    room_state get_room_state()
    {
        return _room_state;
    }

    void add_white_user(uint64_t uid)
    {
        _white_user_id = uid;
        _room_user_nums++;
    }

    void add_black_user(uint64_t uid)
    {
        _black_user_id = uid;
        _room_user_nums++;
    }

    int get_count()
    {
        return _room_user_nums;
    }

    // 获取玩家的id
    uint64_t get_whith_id()
    {
        return _white_user_id;
    }

    uint64_t get_black_id()
    {
        return _black_user_id;
    }

    // 玩家下棋管理
    Json::Value play_chess(Json::Value &req)
    {
        // 1、判断双方是否在线，不在就对方获胜
        Json::Value json_resp = req;
        int row = req["row"].asInt();
        int col = req["col"].asInt();
        uint64_t user_id = req["uid"].asUInt64(); // 下棋用户的id
        if (_onlineUser_manager->is_in_room_user(_white_user_id) == false)
        {
            // 此时白棋用户不在房间中
            json_resp["result"] = true;
            json_resp["reason"] = "你小子运气这么好,对方掉线了,你赢了,呵呵!";
            json_resp["winner"] = (Json::UInt64)_black_user_id;
            return json_resp;
        }

        if (_onlineUser_manager->is_in_room_user(_black_user_id) == false)
        {
            // 此时黑棋用户不在房间中
            json_resp["result"] = true;
            json_resp["reason"] = "你小子运气这么好,对方掉线了,你赢了,呵呵!";
            json_resp["winner"] = (Json::UInt64)_white_user_id;
            return json_resp;
        }

        // 2、判断这步棋是否合法
        if (_board[row][col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置已经被下过了,你再去选个位置下!";
            return json_resp;
        }

        int cur_color = user_id == _white_user_id ? CHESS_WHITE : CHESS_BLACK;
        _board[row][col] = cur_color; // 下棋到数组中
        // 3、检测当前这步棋，是否有获胜的可能（五子连珠）返回值是用户的id - 获胜
        uint64_t winner_id = check_win(row, col, cur_color);
        if (winner_id != 0)
        {
            // 此时有玩家获胜
            json_resp["reason"] = "恭喜你,五星连珠,战胜了对方,牛牛牛!";
        }
        json_resp["result"] = true;
        json_resp["winner"] = (Json::UInt64)winner_id;
        return json_resp;
    }

    // 玩家聊天管理
    Json::Value user_chat(Json::Value &req)
    {
        Json::Value json_rep = req;
        // 判断房间号是否匹配
        uint64_t room_id = req["room_id"].asUInt64();
        if (room_id != _room_id)
        {
            json_rep["result"] = false;
            json_rep["reason"] = "房间号匹配失败!";
            return req;
        }

        std::string message = req["message"].asString();
        // 判断是否包含敏感词
        size_t pos = message.find("废物");
        if (pos != std::string::npos)
        {
            json_rep["result"] = false;
            json_rep["reason"] = "你要发的信息包含敏感词,请重新发送!";
            return json_rep;
        }
        // 广播消息
        json_rep["result"] = true;
        return json_rep;
    }

    // 连接断开，玩家退出房间
    void handle_exit_room(uint64_t id)
    {
        // 判断玩家退出，是否是在游戏中退出（对方获胜），还是游戏结束时退出
        Json::Value resp;
        if (_room_state == GAME_START)
        {
            uint64_t winner = id == _white_user_id ? _black_user_id : _white_user_id;
            // 异常退出
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["reason"] = "对方掉线了,恭喜你侥幸获胜了!";
            resp["room_id"] = (Json::UInt64)_room_id;
            resp["uid"] = (Json::UInt64)id;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] = (Json::UInt64)winner;
            uint64_t loser = winner == _white_user_id ? _black_user_id : _white_user_id;
            _user_exec->win(winner);
            _user_exec->lose(loser);
            _room_state = GAME_OVER;
            // 并进行广播
            broadcast(resp);
        }
        _room_user_nums--;
        return;
    }

    // 动作合法，将用户的动作广播给房间内的玩家
    void broadcast(Json::Value &rep)
    {
        // 1、对要广播给玩家或者用户的消息进行序列化
        std::string message;
        json_util::serialize(rep, message);
        // 2、获取每个玩家的通信连接 白棋 + 黑棋
        // 3、并将消息发送
        wwserver_t::connection_ptr wconn = _onlineUser_manager->get_from_room_con(_white_user_id);
        if (wconn.get() != nullptr)
        {
            wconn->send(message);
        }
        else
        {
            DLOG("房间 - 白棋玩家获取连接失败!");
        }

        wwserver_t::connection_ptr bconn = _onlineUser_manager->get_from_room_con(_black_user_id);
        if (bconn.get() != nullptr)
        {
            bconn->send(message);
        }
        else
        {
            DLOG("房间 - 黑棋玩家获取连接失败!");
        }

        return;
    }

    // 总的处理动作，区分不同的处理函数，调用不同的处理动作，并将处理完的动作结果广播出去
    void handle_total(Json::Value &req)
    {
        Json::Value Json_resp;
        uint64_t room_id = req["room_id"].asUInt64();
        // 判断房间是否匹配
        if (room_id != _room_id)
        {
            Json_resp["optype"] = req["optype"].asString();
            Json_resp["result"] = false;
            Json_resp["reason"] = "房间匹配失败!";
            return broadcast(Json_resp);
        }
        // 判断操作
        if (req["optype"].asString() == "put_chess")
        {
            Json_resp = play_chess(req);
            // 判断是否有玩家获胜,并处理数据
            if (Json_resp["winner"].asUInt64() != 0) // winner是用户ID号
            {
                uint64_t winner = Json_resp["winner"].asUInt64();
                uint64_t loser = winner == _white_user_id ? _black_user_id : _white_user_id;
                _user_exec->win(winner);
                _user_exec->lose(loser);
                _room_state = GAME_OVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            Json_resp = user_chat(req);
        }
        else
        {
            // 一个错误的请求
            Json_resp["optype"] = req["optype"].asString();
            Json_resp["result"] = true;
            Json_resp["reason"] = "未知的请求类型!";
        }
        return broadcast(Json_resp);
    }
};

// 房间管理设计
using room_ptr = std::shared_ptr<RoomOnlineManger>;

class Room_Manager
{
private:
    user_exec *_ue;         // 数据库管理句柄
    OlineUser_Manager *_om; // 在线用户管理句柄
    uint64_t _rid;          // 房间计数器，会自增
    std::mutex _mutex;
    std::unordered_map<uint64_t, room_ptr> _rid_room; // 房间ID找到房间信息  room_id 房间信息
    std::unordered_map<uint64_t, uint64_t> _uid_room; // 用户id找到房间信息  uid room_id
public:
    Room_Manager(user_exec *ue, OlineUser_Manager *om)
        : _rid(1), _ue(ue), _om(om)
    {
        DLOG("房间管理模块初始化完成!");
    }

    ~Room_Manager()
    {
        DLOG("房间管理模块即将销毁!");
    }

    // 创建房间 返回房间信息的智能指针
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 在游戏大厅中的两名玩家匹配成功后，再创建房间
        // 1、判断玩家是否都在游戏大厅中
        if (_om->is_in_hall_user(uid1) == false)
        {
            DLOG("玩家: %lu 不在大厅中,创建房间失败!", uid1);
            return room_ptr();
        }

        if (_om->is_in_hall_user(uid2) == false)
        {
            DLOG("玩家: %lu 不在大厅中,创建房间失败!", uid2);
            return room_ptr();
        }
        // 创建房间，在多线程的情况下，保证线程安全
        std::unique_lock<std::mutex> lock(_mutex);
        // 2、创建游戏房间，并将玩家信息管理起来  将玩家添加进入房间中
        room_ptr rp(new RoomOnlineManger(_rid, _ue, _om));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        // 3、将游戏房间进行管理
        _rid_room.insert(std::make_pair(_rid, rp));
        _uid_room.insert(std::make_pair(uid1, _rid));
        _uid_room.insert(std::make_pair(uid2, _rid));
        _rid++; // 房间号 ++
        // 4、将游戏房间信息返回
        return rp;
    }

    // 通过房间号找到房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto rit = _rid_room.find(rid);
        if (rit == _rid_room.end())
        {
            return room_ptr();
        }
        return rit->second;
    }

    // 通过用户id扎到房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        // 先通过用户id找到房间id，再通过房间id找到房间信息（不能直接调用get_room_by_rid）会造成死锁
        std::unique_lock<std::mutex> lock(_mutex);
        auto uit = _uid_room.find(uid);
        if (uit == _uid_room.end())
        {
            return room_ptr();
        }
        uint64_t room_id = uit->second;
        auto rit = _rid_room.find(room_id);
        if (rit == _rid_room.end())
        {
            return room_ptr();
        }
        return rit->second;
    }

    // 通过房间号销毁房间
    void destory_room_by_rid(uint64_t rid)
    {
        // 房间信息是通过shared_ptr在 room 中管理的，只需要将shared_ptr在room移除
        // 当shared_ptr的计数器为0时，指针就会自动释放

        // 1、通过房间号获取房间信息
        room_ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr) // 房间为空
        {
            return;
        }
        // 2、通过房间信息获取房间中的用户
        uint64_t uid1 = rp->get_whith_id();
        uint64_t uid2 = rp->get_black_id();
        // 3、移除房间管理中的用户信息
        std::unique_lock<std::mutex> lock(_mutex);
        _uid_room.erase(uid1);
        _uid_room.erase(uid2);
        // 4、将房间信息从 _rid_room 中移除
        _rid_room.erase(rid);
    }

    // 删除房间指定用户，当房间中的人全部退出时，销毁房间（连接断开时调用）
    void remove_room_by_uid(uint64_t uid)
    {
        // 1、通过用户id获取房间信息
        room_ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr) // 房间为空
        {
            return;
        }
        // 连接断开时，移除对应的用户
        rp->handle_exit_room(uid);
        // 判断房间中用户的数量是否为0，为0销毁房间
        if (rp->get_count() == 0)
        {
            destory_room_by_rid(rp->get_room_id());
        }
        return;
    }
};